import {
  CommunicationWay,
  ServiceHandler,
  ServiceRegister,
} from "@main/shared/reflect";
import { TYPES } from "@main/shared/types";
import logger from "@shared/logger/main-logger";
import type {
  CreateTabData,
  CreateTabDataWithOrder,
  Tab,
  UpdateTabData,
} from "@shared/triplit/types";
import { inject, injectable } from "inversify";
import type { TabDbService } from "./db-service/tab-db-service";
import { EventNames, emitter, sendToMain } from "./event-service";
import type { ShortcutsService } from "./shortcuts-service";
import type { UiService } from "./ui-service";

@ServiceRegister(TYPES.TabService)
@injectable()
export class TabService {
  constructor(
    @inject(TYPES.TabDbService) private tabDbService: TabDbService,
    @inject(TYPES.UiService) private uiService: UiService,
    @inject(TYPES.ShortcutsService) private shortcutsService: ShortcutsService,
  ) {
    emitter.on(EventNames.THREAD_RENAME, async (event) => {
      await this.handleThreadRename(event);
    });

    emitter.on(EventNames.TAB_SWITCHED, async (event) => {
      await this.handleTabSwitched(event);
    });

    emitter.on(EventNames.TAB_CLOSED, async (event) => {
      await this.handleTabClosed(event);
    });

    emitter.on(EventNames.THREAD_NEW_BRANCH, async (event) => {
      await this.handleThreadNewBranch(event);
    });
  }

  private async handleThreadRename(event: {
    threadId: string;
    newTitle: string;
  }) {
    const tab = await this.tabDbService.getTabByThreadId(event.threadId);
    if (!tab) return;

    await this.tabDbService.updateTab(tab.id, {
      title: event.newTitle,
    });
  }

  private async handleThreadNewBranch(event: {
    title: string;
    newThreadId: string;
    type: Tab["type"];
    isPrivate: boolean;
    isNewTab?: boolean;
  }): Promise<void> {
    const { title, newThreadId, type, isPrivate, isNewTab = true } = event;
    const tab = {
      title,
      threadId: newThreadId,
      type,
      isPrivate,
    };
    if (isNewTab) {
      await this._insertTab(tab);
    } else {
      await this._insertTabInCurrentTab(tab);
    }
  }

  async _insertTabInCurrentTab(tab: CreateTabDataWithOrder): Promise<void> {
    try {
      const currentActiveTabId = await this.uiService.getActiveTabId();
      const currentTab = await this.tabDbService.getTab(currentActiveTabId);
      const currentOrder = currentTab?.order ?? 0;
      await this.tabDbService.deleteTabWithoutReorder(currentActiveTabId);
      const newTab = await this.tabDbService.insertTabWithOrder({
        ...tab,
        order: currentOrder,
      });
      await this.uiService._updateActiveTabId(newTab.id);
    } catch (error) {
      logger.error("TabService:_insertTab error", { error });
      throw error;
    }
  }

  async _insertTab(tab: CreateTabData): Promise<void> {
    try {
      const newTab = await this.tabDbService.insertTab(tab);
      await this.uiService._updateActiveTabId(newTab.id);
    } catch (error) {
      logger.error("TabService:_insertTab error", { error });
      throw error;
    }
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__TWO_WAY)
  async insertTab(
    _event: Electron.IpcMainEvent,
    tab: CreateTabData,
  ): Promise<Tab> {
    try {
      const newTab = await this.tabDbService.insertTab(tab);
      return newTab;
    } catch (error) {
      logger.error("TabService:insertTab error", { error });
      throw error;
    }
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__TWO_WAY)
  async deleteTab(
    _event: Electron.IpcMainEvent,
    tabId: string,
  ): Promise<string> {
    try {
      const currentActiveTabId = await this.tabDbService.deleteTab(tabId);

      sendToMain(EventNames.TAB_CLOSED, { tabId });

      return currentActiveTabId;
    } catch (error) {
      logger.error("TabService:deleteTab error", { error });
      throw error;
    }
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__ONE_WAY)
  async deleteAllTabs(_event: Electron.IpcMainEvent): Promise<void> {
    try {
      await this.tabDbService.deleteAllTabs();
      await this.uiService.clearActiveTabId();
    } catch (error) {
      logger.error("TabService:deleteAllTabs error", { error });
      throw error;
    }
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__ONE_WAY)
  async deleteOtherTabs(
    _event: Electron.IpcMainEvent,
    currentTabId: string,
  ): Promise<void> {
    try {
      await this.tabDbService.deleteOtherTabs(currentTabId);
    } catch (error) {
      logger.error("TabService:deleteOtherTabs error", { error });
      throw error;
    }
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__ONE_WAY)
  async deleteRightTabs(
    _event: Electron.IpcMainEvent,
    currentTabId: string,
  ): Promise<void> {
    try {
      await this.tabDbService.deleteRightTabs(currentTabId);
    } catch (error) {
      logger.error("TabService:deleteRightTabs error", { error });
      throw error;
    }
  }

  async _updateTab(tabId: string, updateData: UpdateTabData): Promise<void> {
    await this.tabDbService.updateTab(tabId, updateData);
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__ONE_WAY)
  async updateTab(
    _event: Electron.IpcMainEvent,
    tabId: string,
    updateData: UpdateTabData,
  ): Promise<void> {
    try {
      await this._updateTab(tabId, updateData);
    } catch (error) {
      logger.error("TabService:updateTab error", { error });
      throw error;
    }
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__TWO_WAY)
  async getTab(
    _event: Electron.IpcMainEvent,
    tabId: string,
  ): Promise<Tab | null> {
    try {
      const tab = await this.tabDbService.getTab(tabId);
      return tab;
    } catch (error) {
      logger.error("TabService:getTab error", { error });
      throw error;
    }
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__ONE_WAY)
  async moveTab(
    _event: Electron.IpcMainEvent,
    fromIndex: number,
    toIndex: number,
    tabs: Tab[],
  ): Promise<void> {
    try {
      await this.tabDbService.moveTab(fromIndex, toIndex, tabs);
    } catch (error) {
      logger.error("TabService:moveTab error", { error });
      throw error;
    }
  }

  async getCurrentActiveTab(): Promise<Tab | null> {
    try {
      const activeTabId = await this.uiService.getActiveTabId();
      if (!activeTabId) return null;

      return await this.tabDbService.getTab(activeTabId);
    } catch (_error) {
      return null;
    }
  }

  async isCurrentTabIframe(): Promise<boolean> {
    const activeTab = await this.getCurrentActiveTab();
    return activeTab?.type === "302ai-tool";
  }

  private async handleTabSwitched(event: {
    previousTabId?: string;
    currentTabId: string;
  }): Promise<void> {
    const currentTab = await this.tabDbService.getTab(event.currentTabId);
    const isCurrentIframe = currentTab?.type === "302ai-tool";

    if (isCurrentIframe) {
      this.shortcutsService.registerIframeGlobalShortcuts();
    } else {
      this.shortcutsService.unregisterIframeGlobalShortcuts();
    }
  }

  private async handleTabClosed(_event: { tabId: string }): Promise<void> {
    const currentActiveTabId = await this.uiService.getActiveTabId();
    if (currentActiveTabId) {
      const currentTab = await this.tabDbService.getTab(currentActiveTabId);
      const isCurrentIframe = currentTab?.type === "302ai-tool";

      if (isCurrentIframe) {
        this.shortcutsService.registerIframeGlobalShortcuts();
      } else {
        this.shortcutsService.unregisterIframeGlobalShortcuts();
      }
    } else {
      this.shortcutsService.unregisterIframeGlobalShortcuts();
    }
  }
}
